/*
 CCCC - C and C++ Code Counter
 Copyright (C) 1994-2005 Tim Littlefair (tim_littlefair@hotmail.com)

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 */
// cccc_utl.cc

// implementation of enumerations and utility classes for CCCC
// includes the Parse_Utility class which is a helper to centralise
// error recovery and recording facilities across the three parsers
#include "cccc.h"

#include "cccc_itm.h"
#include "cccc_utl.h"
#include "cccc_db.h"
#include "cccc_tok.h"
#include "AParser.h"
#include "ATokPtr.h"

#include "cccc_mod_sinfile.h"

#define DEBUG_EXTENT_STREAMS 1

#include <cassert>
#include <iomanip>
using std::ios;
//using std::trunc;
using std::ends;
using std::setw;
using std::setiosflags;
using std::resetiosflags;

#define FS "@"
#define RS "\n"

string ParseUtility::stack_rules[MAX_STACK_DEPTH];
int ParseUtility::stack_tokenline[MAX_STACK_DEPTH];
string ParseUtility::stack_tokentext[MAX_STACK_DEPTH];
int ParseUtility::stack_depth;

ParseUtility* ParseUtility::theCurrentInstance=NULL;
ParseStore* ParseStore::theCurrentInstance=NULL;

// insertion and extraction functions intended to support enumerations
void insert_enum(ostream& os, int e) {
	os << (char) e;
}

void extract_enum(istream& is, int& e) {
	e=0;
	is >> (char&) e;
}

ostream& operator<<(ostream& os, AugmentedBool ab) {
	insert_enum(os, ab);
	return os;
}

istream& operator>>(istream& is, AugmentedBool& ab) {
	extract_enum(is, (int&)ab);
	return is;
}

ostream& operator<<(ostream& os, Visibility v) {
	insert_enum(os, v);
	return os;
}

istream& operator>>(istream& is, Visibility& v) {
	extract_enum(is, (int&)v);
	return is;
}

ostream& operator<<(ostream& os, UseType ut) {
	insert_enum(os, ut);
	return os;
}

istream& operator>>(istream& is, UseType& ut) {
	extract_enum(is, (int&)ut);
	return is;
}

string ParseUtility::lookahead_text(int n) {
	static string retval;
	retval="";
	int i;
	for (i=1; i<=n; i++) {
		if (parser->LT(i) != NULL) {
			retval=retval+parser->LT(i)->getText();
			retval=retval+" ";
		}
	}
	return retval;
}

void ParseUtility::resynchronize(int initial_nesting,
		SetWordType *resync_token_class, ANTLRTokenPtr& resync_token) {
	// the interface for resynchronisation is as follows:
	// the caller supplies a nesting level at which the resynchronisation must 
	// occur, and a token class containing all of the tokens which can 
	// be accepted to delimit the resynchronisation
	// this function will scan until it finds that it is at the correct level and
	// the next token of lookahead is in the resynchronisation set
	// it will then accept as many tokens from the resynchronisation set as
	// are available, consolidating the text of the tokens accepted
	// as the text associated with the last token
	string resync_text="...";

	string string1=parser->LT(1)->getText();
	int line1=parser->LT(1)->getLine();
	string string2;
	int line2=0;

	int resynchronising=1;
	while (resynchronising) {
		parser->consumeUntil(resync_token_class);
		if ( (MY_TOK(parser->LT(1))->getNestingLevel()> initial_nesting) && (parser->LT(2) != NULL)) {
			parser->consume();
		} else {
			// we are ready to resynchronise
			resynchronising=0;
			string2=parser->LT(1)->getText();
			line2=parser->LT(1)->getLine();
		}
	}

	// we now consume a succession of tokens from the resynchronisation token
	// class until we come across a token which is not in the set, or the
	// nesting level changes
	resync_token=parser->LT(1);
	while (parser->set_el(parser->LT(1)->getType(), resync_token_class) && ( MY_TOK(parser->LT(1))->getNestingLevel() == initial_nesting)) {
		string2=parser->LT(1)->getText();
		line2=parser->LT(1)->getLine();

		resync_text+=parser->LT(1)->getText();
		resync_text+=" ";
		resync_token=parser->LT(1);
		resync_token->setText(resync_text.c_str());
		parser->consume();
	}

	cerr << "Unrecognized section from " << string1.c_str() << " on line "
			<< line1 << " to " << string2.c_str() << " on line " << line2
			<< endl << "=====ignored section begins=====" << endl
			<< resync_text.c_str() << endl
			<< "===== ignored section ends =====" << endl;
}

ParseUtility::ParseUtility(ANTLRParser *parser) {
	// This is designed as a serial-singleton class (e.g. many 
	// instances may exist over time but no more than one at a
	// time).
	// For the lifetime of an instance, the static member theCurrentInstance
	// points to it. When no instance exists, this pointer is null.
	assert(theCurrentInstance==NULL);
	theCurrentInstance=this;

	trace_depth=0;
	stack_depth=0;
	this->parser=(ANTLR_Assisted_Parser*)parser;

}

ParseUtility::~ParseUtility() {
	theCurrentInstance=NULL;
}

// This utility function is used to create
// a composite scope name from a qualifier scope
// and a relative name.
string ParseUtility::scopeCombine(const string& baseScope, const string& name) {
	// I am presently (as at 3.pre44) experimenting with
	// how I handle scopes.  The present code has a policy
	// of discarding scope information altogether and defining
	// modules based solely on the final component of the 
	// fully qualified name.
	// This variable may become a parameter to control policy in this
	// area.
	bool bIgnoreScope=true;
	string retval;
	if (bIgnoreScope) {
		retval=name;
	} else if (baseScope.size()>0 && name.size()>0) {
		retval=baseScope+"::"+name;
	} else {
		retval=baseScope+name;
	}

	return retval;
}

ParseStore::ParseStore(const string& filename) :
	theFilename(filename), pendingLexicalCounts(static_cast<int>(tcLAST), 0),
			flag(static_cast<int>(psfLAST)+1, '?') {
	// This is designed as a serial-singleton class (e.g. many 
	// instances may exist over time but no more than one at a
	// time).
	// For the lifetime of an instance, the static member theCurrentInstance
	// points to it. When no instance exists, this pointer is null.
	assert(theCurrentInstance==NULL);
	theCurrentInstance=this;
	flag[psfLAST]='\0';

	speIFDEF=0;

}

ParseStore::~ParseStore() {
	// If the current object came from the default constructor
	// it is the primary singleton instance and we wish to 
	// set the static pointer to itself back to null.  Otherwise,
	// it was a cached copy, and we don't really care.
	if (theCurrentInstance==this) {
		theCurrentInstance=NULL;
	}
}

int ParseStore::get_flag(PSFlag psf) const {
	return int(flag[psf]);
}

void ParseStore::set_flag(PSFlag psf, int value) {
	flag[psf]=value;
}

void ParseStore::set_flag(Visibility value) {
	MAKE_STRSTREAM(ofstr);
	ofstr << value;
	flag[psfVISIBILITY]=(ofstr.str())[0];RELEASE_STRSTREAM(ofstr);
}

Visibility ParseStore::get_visibility() {
	return static_cast<Visibility>(flag[psfVISIBILITY]);
}

string ParseStore::filename() {
	return theFilename;
}

void ParseStore::insert_extent(CCCC_Item& os, int startLine, int endLine,
		const string& description, const string& flags, UseType ut,
		bool allocate_lexcounts) {
	os.Insert(theFilename);
	os.Insert(startLine);
	os.Insert(description);
	os.Insert(flags);
	int i;
	int lexical_counts_for_this_extent[tcLAST];
	for (i=0; i<tcLAST; i++) {
		lexical_counts_for_this_extent[i]=0;
	}

	if (allocate_lexcounts==true) {
		LineLexicalCountMatrix::iterator extentStartIter =
				lineLexicalCounts.lower_bound(startLine);
		LineLexicalCountMatrix::iterator extentEndIter =
				lineLexicalCounts.upper_bound(endLine-1);
		LineLexicalCountMatrix::iterator llcmIter;
		for (llcmIter=extentStartIter; llcmIter!=extentEndIter; ++llcmIter) {
			// This record relates to a line within the current
			// extent.
			for (i=0; i<tcLAST; i++) {
				lexical_counts_for_this_extent[i]+=(*llcmIter).second[i];
			}
		}
		// The lexical occurrences mentioned in the records processed 
		// above are now been accounted for in the database, so we
		// purge these records.  This has the effect of allowing
		// accurate accounting on nested extents (i.e. the outer
		// extent will only be reported as containing lines which 
		// are not already listed in the inner extent).
		lineLexicalCounts.erase(extentStartIter, extentEndIter);

		ostringstream lexcount_str;

		lexcount_str << "LOC:" << lexical_counts_for_this_extent[tcCODELINES]
				<< " COM:" << lexical_counts_for_this_extent[tcCOMLINES]
				<< " MVG:" << lexical_counts_for_this_extent[tcMCCABES_VG]
				<< ends;

		os.Insert(lexcount_str.str().c_str());

	} else {
		os.Insert("*");
	}
	os.Insert((char)flag[psfVISIBILITY]);
	os.Insert((char)ut);
}

void ParseStore::record_module_extent(int startLine, int endLine,
		const string& moduleName, const string& moduleType,
		const string& description, UseType ut) {
	// See the lengthy comment in record_userel_extent about
	// why we are filtering for empty module names.
	if (moduleName.size()>0) {
		CCCC_Item module_line;
		module_line.Insert(moduleName);
		module_line.Insert(moduleType);
		insert_extent(module_line, startLine, endLine, description, flags(),
				ut, true);
		prj->add_module(module_line);
	}
}

void ParseStore::record_function_extent(int startLine, int endLine,
		const string& returnType, const string& moduleName,
		const string& memberName, const string& paramList,
		const string& description, Visibility visibility, UseType ut) {
	// We require every call to this function to specify a member
	// function name and a parameter list.
	if (memberName.size()>0) {
		// If the moduleName is an empty string, we remap this to the
		// string "anonymous".  This implies that we treat all
		// C-style functions as belonging to a single module.
		string mappedModuleName = moduleName;
		if (mappedModuleName.size()==0) {
			mappedModuleName = "anonymous";
		}

		CCCC_Item function_line;
		function_line.Insert(mappedModuleName);
		function_line.Insert(memberName);
		function_line.Insert(returnType);
		function_line.Insert(paramList);

		string baseFlags=flags();
		baseFlags[psfVISIBILITY]=visibility;

		insert_extent(function_line, startLine, endLine, description,
				baseFlags, ut, true);
		prj->add_member(function_line);
	}
}

void ParseStore::record_userel_extent(int startLine, int endLine,
		const string& clientName, const string& memberName,
		const string& serverName, const string& description,
		Visibility visibility, UseType ut) {
	CCCC_Item userel_line;

	// This function should not be invoked unless the clientName
	// and serverName are non-empty strings, however it appears
	// that in test case prn16.java the parser does execute the 
	// actions of the 'implementsClause' rule, even though there
	// is no 'implements' keyword outside comments in the program
	// text.
	// I don't understand this, but as a workaround, I filter at 
	// this point and ensure that if either clientName or serverName
	// is empty, no action is taken.
	if (clientName.size()>0 && serverName.size()>0) {
		userel_line.Insert(clientName);
		userel_line.Insert(memberName);
		userel_line.Insert(serverName);

		// for data member definitions, we record lexical data for the
		// extent,
		// for inheritance and parameter relationships we do not
		bool record_lexcounts=false;
		switch (ut) {
		case utHASBYVAL:
		case utHASBYREF:
			record_lexcounts=true;
			break;
		default:
			record_lexcounts=false;
		}

		string baseFlags=flags();
		baseFlags[psfVISIBILITY]=visibility;
		insert_extent(userel_line, startLine, endLine, description, baseFlags,
				ut, record_lexcounts);
		prj->add_userel(userel_line);
	}
}

void ParseStore::record_other_extent(int startLine, int endLine,
		const string& description) {
	CCCC_Item rejext_line;
	insert_extent(rejext_line, startLine, endLine, description, flags(),
			utREJECTED, true);
	prj->add_rejected_extent(rejext_line);
}

static void toktrace(ANTLRAbstractToken *tok) {
	// at the LHS we put out information about the current token
	if (tok != NULL) {
		DbgMsg(PARSER,cerr,
				std::setw(6) << tok->getLine()
				<< std::setw(4) << (int)tok->getType()
				<< std::setiosflags(ios::left)
				<< std::resetiosflags(ios::right)
				<< std::setw(20) << tok->getText()
		);
	} else {
		DbgMsg(PARSER,cerr,std::setw(30)<<"");
	}
}

enum InOrOut {IO_IN, IO_OUT};

static void rectrace(const char *rulename, const char *dir_indic, int guessing,
		ANTLRAbstractToken *tok) {
	static int trace_depth=0;
	if (guessing) {
		DbgMsg(PARSER,cerr,
				setw(trace_depth*4+1) << "" << dir_indic
				<< "?" << rulename << endl);
	} else {
		trace_depth=((ANTLRToken*) tok)->getNestingLevel();
		DbgMsg(PARSER,cerr,
				setw(trace_depth*4)<< "" << dir_indic << rulename << endl);
	}
}

void ParseUtility::tracein(const char *rulename, int guessing,
		ANTLRAbstractToken *tok) {
	if (guessing == 0) {
		stack_tokentext[stack_depth]=tok->getText();
		stack_tokenline[stack_depth]=tok->getLine();
		stack_rules[stack_depth]=rulename;
		stack_depth++;
	}

	// first put out the token details
	toktrace(tok);

	// then the indented recognition trace
	rectrace(rulename, "-> ", guessing, tok);
}

void ParseUtility::traceout(const char *rulename, int guessing,
		ANTLRAbstractToken *tok) {
	if (guessing == 0) {
		stack_depth--;
		// some error checking...
		if (stack_depth<0) {
			cerr << "ParseUtility::traceout negative stack depth - "
					<< "exiting from rule " << rulename << " at "
					<< tok->getText() << " on line " << tok->getLine() << endl;
		} else if (rulename!=stack_rules[stack_depth]) {
			cerr << "ParseStore::traceout rule name mismatch - " << rulename
					<< "!=" << stack_rules[stack_depth] << endl;
		}
		stack_tokentext[stack_depth]="";
		stack_tokenline[stack_depth]=0;
		stack_rules[stack_depth]="";
	}
	// first put out the token details
	toktrace(tok);
	rectrace(rulename, "<- ", guessing, tok);
}

void ParseUtility::syn(_ANTLRTokenPtr tok, ANTLRChar *egroup,
		SetWordType *eset, ANTLRTokenType etok, int k) {
	string filename=ParseStore::currentInstance()->filename();
	if (tok != NULL) {
		cerr << filename << '(' << tok->getLine() << "):"
				<< " syntax error at token " << tok->getText() << endl;
	} else {
		cerr << filename << "(0): syntax error at null token" << endl;
	}

#if 1
	// The logic in the other half of this #if section
	// generated too much noise for some people's taste.
	// It's only really useful to myself (TJL) or anyone
	// else with a taste for debugging cccc.g/java.g etc.
	int i=stack_depth-1;
	cerr << filename << '(' << stack_tokenline[i] << "): trying to match "
			<< stack_rules[i] << " at '" << stack_tokentext[i] << "'" << endl;
#else
	cerr << "Parser context:" << endl;
	for(int i=stack_depth-1; i>=0; i--)
	{
		cerr << filename << '(' << stack_tokenline[i]
		<< "): trying to match " << stack_rules[i]
		<< " at '" << stack_tokentext[i] << "'"
		<< endl;
	}
	cerr << endl;
#endif
}

void ParseStore::endOfLine(int line) {
	// We only do the processing below if the line which has just
	// ended contained at least one non-skippable token
	// The flag which tells us whether this is true is set in the 
	// token constructor
	if (ANTLRToken::bCodeLine) {
		pendingLexicalCounts[tcCODELINES]++;
		LineLexicalCountMatrix::value_type vt(line, LexicalCountArray(
				static_cast<int>(tcLAST), 0));

		for (int i=0; i<tcLAST; i++) {
			vt.second[i]=pendingLexicalCounts[i];
			pendingLexicalCounts[i]=0;
		}
		lineLexicalCounts.insert(vt);

		// reset the flat for next time
		ANTLRToken::bCodeLine=false;
	}


}

void ParseStore::getStat(int startLine,int endLine,int* codeLine,int* comLine){


	LineLexicalCountMatrix::iterator extentStartIter =
					lineLexicalCounts.lower_bound(startLine);
			LineLexicalCountMatrix::iterator extentEndIter =
					lineLexicalCounts.upper_bound(endLine-1);
			LineLexicalCountMatrix::iterator llcmIter;
			for (llcmIter=extentStartIter; llcmIter!=extentEndIter; ++llcmIter) {
				// This record relates to a line within the current
				// extent.
			//	for (i=0; i<tcLAST; i++) {
			//		lexical_counts_for_this_extent[i]+=(*llcmIter).second[i];
			//	}
			//	cout<<"J TEST : Gathering info at line "<<(*llcmIter).first<<endl;
			//	cout<<"J TEST TEST : tcCODELINES"<<(*llcmIter).second[lc]<<endl;
				*codeLine+=(*llcmIter).second[tcCODELINES];
				*comLine +=(*llcmIter).second[tcCOMLINES];
			}
	return ;
}

void ParseStore::getFunc(int startLine,int endLine,Func* func){

	int mvg=0;

	LineLexicalCountMatrix::iterator extentStartIter =
						lineLexicalCounts.lower_bound(startLine);
				LineLexicalCountMatrix::iterator extentEndIter =
						lineLexicalCounts.upper_bound(endLine-1);
				LineLexicalCountMatrix::iterator llcmIter;
				for (llcmIter=extentStartIter; llcmIter!=extentEndIter; ++llcmIter) {
					// This record relates to a line within the current
					// extent.
				//	for (i=0; i<tcLAST; i++) {
				//		lexical_counts_for_this_extent[i]+=(*llcmIter).second[i];
				//	}
				//	cout<<"J TEST : Gathering info at line "<<(*llcmIter).first<<endl;
				//	cout<<"J TEST TEST : tcCODELINES"<<(*llcmIter).second[lc]<<endl;
					mvg +=(*llcmIter).second[tcMCCABES_VG];
				}
	func->setStartLine(startLine);
	func->setMvg(mvg);

	return;
}


